Перейти к основному содержимому

3.07. Транзакции и блокировки

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Транзакции и блокировки

О блокировках

Блокировки в базах данных применяются для обеспечения согласованности данных при параллельном доступе нескольких транзакций. Уровень блокировки определяет объём ресурса, который изолируется от конкурентных изменений. Основные уровни блокировок — блокировка таблицы, страницы и записи (строки). Каждый из них представляет собой компромисс между производительностью, параллелизмом и накладными расходами.

1. Блокировка записи (Row-level locking)

На этом уровне блокируется отдельная строка таблицы. Это наиболее гранулярный тип блокировки, обеспечивающий высокий уровень параллелизма.

Пример использования:

UPDATE employees SET salary = salary * 1.1 WHERE id = 101;

СУБД заблокирует только строку с id = 101, остальные строки остаются доступны для изменений.

Поддержка: InnoDB (MySQL), PostgreSQL, Oracle, SQL Server.

2. Блокировка страницы (Page-level locking)

Страница — это минимальная единица хранения данных на диске (обычно 8 КБ или 16 КБ). При блокировке страницы все строки, находящиеся на этой странице, становятся недоступными для модификации конкурентными транзакциями.

Реже встречается в современных СУБД. Например, некоторые режимы работы в SQL Server или устаревшие движки MySQL (MyISAM не поддерживает блокировку строк).

3. Блокировка таблицы (Table-level locking)

При этом виде блокировки вся таблица целиком становится недоступной для изменений (или чтения, в зависимости от типа блокировки) для других транзакций.

Пример:

LOCK TABLES employees WRITE;
UPDATE employees SET salary = salary + 1000;
UNLOCK TABLES;

Также неявная блокировка таблицы может происходить при выполнении DDL-операций (ALTER TABLE) или при использовании движков, не поддерживающих блокировку строк (например, MyISAM в MySQL).

Современные системы стремятся использовать блокировку строк как стандарт для транзакционных систем, так как она обеспечивает наилучший баланс между масштабируемостью и целостностью данных. Блокировка таблицы допустима в случаях массовой загрузки или обслуживания, но не рекомендуется в активных OLTP-средах.


О транзакциях

Транзакции – это фундаментальная концепция баз данных, которая обеспечивает надёжную обработку данных. ACID – это набор свойств, гарантирующих корректность операций даже при сбоях.

Транзакция – это последовательность SQL-операций, которая выполняется как единое целое. Либо все операции выполняются успешно, либо одна из них не применяется.

Особенность в том, что БД – набор данных, и кода мы поработаем, внесём изменения, они не будут видны другим, пока мы не нажмём «Сохранить» (в SQL это COMMIT). Если мы хотим отменить изменения – ROLLBACK, и правки исчезнут. Это нужно, чтобы не было хаоса, пока два и более человека меняют одни данные одновременно, или если произойдёт сбой во время важной операции.

Большинство СУБД и инструментов по умолчанию используют автокоммит – каждый наш SQL-запрос (INSERT, UPDATE, DELETE) автоматически становится отдельной транзакцией и сразу применяется (COMMIT). Пример:

UPDATE users SET name = 'Анна' WHERE id = 1;  -- СУБД сама делает COMMIT

В GUI СУБД (Oracle, DBeaver, DataGrip) есть кнопки, к примеру, Commit или Rollback – ручное управление транзакциями. Пока мы не нажмём на Commit, другие пользователи не увидят наших изменений. Когда мы пишем сторонний код, допустим, на C#, то по умолчанию автокоммит тоже включен.

Автокоммит не подходит для связанных операций, где важно, чтобы:

  • либо выполнились все шаги, либо ни один;
  • другие пользователи не видели «промежуточных» данных.

Примеры:

Без транзакции:

-- Шаг 1: Списать 100₽ счёта А
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

-- Шаг 2: Зачислить 100₽ на счёт Б
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

С транзакцией:

BEGIN TRANSACTION;  -- Отключаем автокоммит

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

COMMIT; -- Подтверждаем оба изменения
-- или ROLLBACK; в случае ошибки

Таким образом, транзакции - «безопасный режим» для важных операций. Если вам, в целом, нужно просто обучение на SQL, вы и не столкнётесь с транзакциями, когда просто надо сделать, к примеру, обычные SELECT/INSERT. Но когда дело касается сложных вычислений и огромных операций, то, чтобы избежать «частичных изменений», конфликтов и промежуточных правок, нужно выполнять всё целиком – тогда и нужны транзакции.


ACID

ACID – это расшифрока из четырёх ключевых свойств:

СвойствоОписание
Atomicity (Атомарность)Все операции в транзакции выполняются как единое целое. Если одна операция отменена, отменяются все.
Consistecy (Согласованность)Данные переходят из одного корректного состояния в другое. Баланс счетов не может быть отрицательным, например.
Isolation (Изолированность)Параллельные транзакции не мешают друг другу. К примеру, два перевода одних денег не создают конфликт.
Durability (Долговечность)Результаты завершённых транзакций сохраняются даже при сбоях. Пример – после подтверждения перевод не пропадёт при отключении питания.

Пример синтаксиса:

START TRANSACTION;  -- Начало транзакции

-- SQL-операции
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;

-- Завершение транзакции
COMMIT; -- Подтверждение изменений
-- или
ROLLBACK; -- Отмена всех изменений в транзакции

Разные СУБД поддерживают различные уровни изоляции (от самого слабого к самому строгому:

  • Read Uncommited – можно читать «грязные» (незафиксированные) данные;
  • Read Commited – читаются только подтверждённые данные (стандарт PostgreSQL);
  • Repeatable Read – гарантирует, что повторное чтение даст те же данные (стандарт MySQL);
  • Serializable – полная изоляция, как последовательное выполнение.

MySQL/InnoDB поддерживают все уровни ACID;

PostgreSQL поддерживает все уровни ACID;

SQL Server имеет расширенные возможности;

SQLite – ACID поддерживается, но весь файл БД блокируется при записи.